#pragma once

#include <iostream>
#include <memory>
#include <string>
#include <sstream>
#include <ctime>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
#include <filesystem>
#include <fstream>
// 责任链基类
class HandlerText
{
public:
    virtual void Execute(const std::string &text) = 0;
    void SetNext(std::shared_ptr<HandlerText> next)
    {
        _next = next;
    }
    // 开启责任链节点
    void Enable()
    {
        _enable = true;
    }
    // 关闭责任链节点
    void Disable()
    {
        _enable = false;
    }
    virtual ~HandlerText() {}

protected:                              // protected，需要被子类继承看到
    std::shared_ptr<HandlerText> _next; // 下一个责任链节点
    bool _enable = true;                // 是否启用该节点
};

// 对文本进行格式化处理的类
class HandlerTextFormat : public HandlerText
{
public:
    void Execute(const std::string &text) override
    {
        std::string format_result = text;
        if (_enable)
        {
            // 该节点被开启，对文本进行格式化处理
            std::stringstream ss;
            ss << time(nullptr) << "-" << getpid() << "-" << text << "\n";
            format_result = ss.str();
            std::cout << "step 1: 格式化处理: " << text << " 得到: " << format_result << std::endl;
        }
        if (_next)
        {
            _next->Execute(format_result); // 我们需要将处理结果，表现在text内部，传递给下一个节点进行进一步处理
        }
        else
        {
            std::cout << "到达责任链处理结尾，完成责任链处理" << std::endl;
        }
    }
};
// 文件基本信息：文件路径，文件名称
std::string defaultfilepath = "./tmp/";
std::string defaultfilename = "text.log";

// 对文本进行文件保存
class HandlerTextSaveFile : public HandlerText
{
public:
    HandlerTextSaveFile(const std::string &filepath = defaultfilepath,
                        const std::string &filename = defaultfilename)
        : _filepath(filepath), _filename(filename)
    {
        // 形成默认的目录名，filesystem
        if (std::filesystem::exists(_filepath))
        {
            return;
        }
        try
        {
            std::filesystem::create_directories(_filepath);
        }
        catch (std::filesystem::filesystem_error const &e)
        {
            std::cerr << e.what() << '\n';
        }
    }
    void Execute(const std::string &text) override
    {
        std::string format_result = text;
        if (_enable)
        {
            // 保存到指定文件中
            std::string file = _filepath + _filename;
            std::ofstream ofs(file, std::ios::app);
            if (!ofs.is_open())
            {
                std::cerr << "open file: " << file << " failed" << std::endl;
                return;
            }
            ofs << text;
            ofs.close();
            std::cout << "step 2: 保存文件: " << file << " 内容: " << text << std::endl;
        }
        if (_next)
        {
            _next->Execute(format_result); // 我们需要将处理结果，表现在text内部，传递给下一个节点进行进一步处理
        }
        else
        {
            std::cout << "到达责任链处理结尾，完成责任链处理" << std::endl;
        }
    }

private:
    std::string _filepath;
    std::string _filename;
};

const int defaultmaxline = 5; // 最大行数，这里设置的比较短是为了方便测试
// 对文件内容长度进行检查，如果长度过长，则对文件进行打包备份
class HandlerTextBackup : public HandlerText
{
public:
    HandlerTextBackup(const std::string &filepath = defaultfilepath,
                      const std::string &filename = defaultfilename,
                      const int &maxline = defaultmaxline)
        : _filepath(filepath), _filename(filename), _maxline(maxline)
    {
    }
    void Execute(const std::string &text) override
    {
        if (_enable)
        {
            // 该节点被开启，对文件进行检查，如果超范围，我们就要切片，并且进行打包备份
            std::cout << "step 3: 检查文件长度: " << _filepath << _filename << std::endl;
            std::string file = _filepath + _filename;
            if(IsOutOfRange(file))
            {
                // 如果超了范围，进行切片备份
                Backup(file);
            }
        }
        if (_next)
        {
            _next->Execute(text); // 我们需要将处理结果，表现在text内部，传递给下一个节点进行进一步处理
        }
        else
        {
            std::cout << "到达责任链处理结尾，完成责任链处理" << std::endl;
        }
    }
private:
    bool IsOutOfRange(const std::string &file)
    {
        std::ifstream ifs(file);
        if(!ifs.is_open())
        {
            std::cerr << "open file: " << file << " failed" << std::endl;
            return false;
        }
        int lines = 0;
        std::string line;
        while(std::getline(ifs, line))
        {
            lines++;
        }
        ifs.close();
        return lines > _maxline;
    }
    void Backup(const std::string &file)
    {
        std::string suffix = std::to_string(time(nullptr));
        std::string backup_file = file + "." + suffix; // 备份文件名
        // 只需要文件名，不需要路径
        std::string src_file = _filename + "." + suffix;
        std::string tar_file = src_file + ".tgz"; // 打包文件名     
        pid_t pid = fork();

        // 子进程：切片备份并打包
        if(pid == 0)
        {
            // child
            // 1.对文件进行重命名，linux上，对文件进行重命名是原子的
            // 2.我们要让子进程来对文件进行备份
            std::filesystem::rename(file, backup_file);
            std::cout << "step 4: 备份文件: " << file << " 到文件: " << backup_file << std::endl;
            // 3.1 对备份文件进行打包 .tgz
            // 3.1.1 更改工作路径
            std::filesystem::current_path(_filepath);
            // 3.1.2 调用tar命令进行打包
            execlp("tar", "tar", "-czf", tar_file.c_str(), src_file.c_str(), nullptr);
            // 3.2 删除备份文件,但是上面子进程已经做了程序替换了，下面的代码不会执行了
            // 所以删除备份文件，只能由父进程来做
            exit(1); // 防止程序替换失败，要退出子进程
        }
        
        int status;
        pid_t rid = waitpid(pid, &status, 0); // 等待子进程结束
        if(rid > 0)
        {
            // 等待子进程成功
            if(WIFEXITED(status) && WEXITSTATUS(status) == 0)
            {
                // 子进程打包成功，删除源备份文件
                // backup_file是带路径的
                std::filesystem::remove(backup_file);
                std::cout << "step 5: 删除备份文件: " << backup_file << std::endl;
            }
        }
    }
private:
    std::string _filepath;
    std::string _filename;
    int _maxline; // 最大行数
};

// 责任链入口类
class HandlerEntry
{
public:
    HandlerEntry()
    {
        // 构造责任链节点
        _format = std::make_shared<HandlerTextFormat>();
        _save = std::make_shared<HandlerTextSaveFile>();
        _backup = std::make_shared<HandlerTextBackup>();

        // 设置责任链处理顺序
        _format->SetNext(_save);
        _save->SetNext(_backup);
    }
    void EnableHandler(bool isformat, bool issave, bool isbackup)
    {
        isformat ? _format->Enable() : _format->Disable();
        issave ? _save->Enable() : _save->Disable();
        isbackup ? _backup->Enable() : _backup->Disable();
    }
    // 参数是需要处理的字符串
    void Run(const std::string &text)
    {
        _format->Execute(text);
    }
    ~HandlerEntry() {}

private:
    std::shared_ptr<HandlerText> _format;
    std::shared_ptr<HandlerText> _save;
    std::shared_ptr<HandlerText> _backup;
};